package com.spacing.app;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.google.analytics.tracking.android.EasyTracker;
import com.google.analytics.tracking.android.Tracker;
import com.senseilearning.spanish.R;
import com.spacing.app.data.Section;
import com.spacing.app.data.Word;
import com.spacing.app.Spacer;

public class SpeechActivity extends SherlockActivity implements RecognitionListener, OnInitListener {
	
	private Question q;
	private Spacer spacer;
	private Section section;
	private String word;
	private String[] answer;
	private static final String[] special_char = new String[] { "!", ",", "(", ")", "?", ":", ";", ",", ".", "¿", "¡" };
	
	private TextView textViewStack;
	private TextView hint_tv;
	private TextView input;
	private TextView instructions;
	
	private CountDownTimer countDownTimer;
	private CountDownTimer countDownTimeOut;
	
	private Animation shake;
	
	private boolean doubleBackToExitPressedOnce;
	private boolean stop = true;
	private boolean sound;
	private boolean canSkip;
	private boolean useTTS;
	
	private Intent intent;
	private SpeechRecognizer speech;
	
	private TextToSpeech tts;
	private int MY_DATA_CHECK_CODE = 0;
	
	private ImageView mic;
	private CircleView view;
	
	private ArrayList<Word> words;
	
	private MenuItem soundOn;
	private MenuItem soundOff;
	private MenuItem skip;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FULLSCREEN);
		ActionBar ab = getSupportActionBar();
		ab.setTitle(getString(R.string.speech));
		super.onCreate(savedInstanceState);
		setContentView(R.layout.speech);
		view = (CircleView) findViewById(R.id.circleview);
		mic = (ImageView) findViewById(R.id.mic1);
		view.height=mic.getHeight();
		view.width=mic.getWidth();
		intent = getIntent();
		section = (Section) intent.getSerializableExtra("section");
		intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
		intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, "ru");
		intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "ru");
		intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
		intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, this.getPackageName());
		intent.putExtra(RecognizerIntent.EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE, "true");
		if (!SpeechRecognizer.isRecognitionAvailable(this)) {
			showNoSpeechDialog();
		}
		speech = SpeechRecognizer.createSpeechRecognizer(this);
		speech.setRecognitionListener(this);
		tts = new TextToSpeech(this, this);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		Intent checkTTSIntent = new Intent();
		checkTTSIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
		startActivityForResult(checkTTSIntent, MY_DATA_CHECK_CODE);
		words = section.getWords();
		spacer = new Spacer(section.getWords(), new SchemaHelper(this), "Speech");
		q = new Question(section, spacer);
		hint_tv = (TextView) findViewById(R.id.hint);
		instructions = (TextView) findViewById(R.id.instruction);
		input = (TextView) findViewById(R.id.answer);
		
		textViewStack = (TextView) findViewById(R.id.stack);
		shake = AnimationUtils.loadAnimation(this, R.anim.shake);
		AnimationUtils.loadAnimation(this, R.anim.fade);
		AnimationUtils.loadAnimation(this, R.anim.fadeout);
		setQuestion();

	}

	//onStart and onStop are now only used for calling the analytics methods
	@Override
	public void onStart(){
		super.onStart();
		//New code for this method must be added before the easytracker line!
		EasyTracker.getInstance().activityStart(this);
		Tracker tracker = EasyTracker.getTracker();
		tracker.setCustomDimension(2, section.getName()); //misschien ook een chapter meegeven? dimension id voor een chapter is 1, voor een section 2.
		tracker.sendView();
	}
	
	@Override
	public void onStop() {
		super.onStop();
		//New code for this method must be added before the easytracker line!
		EasyTracker.getInstance().activityStop(this);
	}
	
	
	private void showNoSpeechDialog() {
		DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				switch (which) {
				case DialogInterface.BUTTON_POSITIVE:
					// Yes button clicked
					finish();
					break;
				}
			}
		};
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(getString(R.string.geenmic)).setCancelable(false)
				.setPositiveButton("OK", dialogClickListener).show();
	}

	private void showNoInternetDialog() {
		DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				switch (which) {
				case DialogInterface.BUTTON_POSITIVE:
					// Yes button clicked
					finish();
					break;
				case DialogInterface.BUTTON_NEGATIVE:
					// No button clicked
					speech.startListening(intent);
					break;
				}
			}
		};
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(getString(R.string.nointernet))
				.setPositiveButton(getString(R.string.stoppen), dialogClickListener)
				.setNegativeButton(getString(R.string.again), dialogClickListener).show();
	}

	private void setQuestion() {
		canSkip = true;
		q.nextQuestion();
		word = q.questions[0][0];
		removeAccents(q.questions[0][0].toUpperCase(Locale.ENGLISH));
		hint_tv.setText(q.questions[1][0]);// + " " + word);
		answer = new String[word.length()];
		Arrays.fill(answer, "__");
		input.setTextColor(getResources().getColor(R.color.black));
		input.setBackgroundColor(getResources().getColor(R.color.white));
		input.setText("");
		textViewStack.setText("" + spacer.getNumberInStack() + "/" + section.size() + " " + getString(R.string.stack));
		spacer.startOfEventTime = System.currentTimeMillis(); 
		if (speech != null) {
			speech.startListening(intent);
			instructions.setText(R.string.initializing);
		}
	}

	public void clickNext(View view) {
		if (spacer.isComplete() && stop) {
			showDialogSessionComplete();
		}
		setQuestion();
	}

	public void clickNext() {
		setQuestion();
	}

	private static final String PLAIN_ASCII = "AaEeIiOoUu" // grave
			+ "AaEeIiOoUuYy" // acute
			+ "AaEeIiOoUuYy" // circumflex
			+ "AaOoNn" // tilde
			+ "AaEeIiOoUuYy" // umlaut
			+ "Aa" // ring
			+ "Cc" // cedilla
			+ "OoUu" // double acute
	;

	private static final String UNICODE = "\u00C0\u00E0\u00C8\u00E8\u00CC\u00EC\u00D2\u00F2\u00D9\u00F9"
			+ "\u00C1\u00E1\u00C9\u00E9\u00CD\u00ED\u00D3\u00F3\u00DA\u00FA\u00DD\u00FD"
			+ "\u00C2\u00E2\u00CA\u00EA\u00CE\u00EE\u00D4\u00F4\u00DB\u00FB\u0176\u0177"
			+ "\u00C3\u00E3\u00D5\u00F5\u00D1\u00F1"
			+ "\u00C4\u00E4\u00CB\u00EB\u00CF\u00EF\u00D6\u00F6\u00DC\u00FC\u0178\u00FF" + "\u00C5\u00E5"
			+ "\u00C7\u00E7" + "\u0150\u0151\u0170\u0171";

	/**
	 * remove accented from a string and replace with ascii equivalent
	 */
	private static String removeAccents(String s) {
		if (s == null)
			return null;
		StringBuilder sb = new StringBuilder(s.length());
		int n = s.length();
		int pos = -1;
		char c;
		boolean found = false;
		for (int i = 0; i < n; i++) {
			pos = -1;
			c = s.charAt(i);
			pos = (c <= 126) ? -1 : UNICODE.indexOf(c);
			if (pos > -1) {
				found = true;
				sb.append(PLAIN_ASCII.charAt(pos));
			} else {
				sb.append(c);
			}
		}
		if (!found) {
			return s;
		} else {
			return sb.toString();
		}
	}

	private void correctAnswer(float tmp) {

		if (tmp > 0.8) {
			instructions.setText(getResources().getString(R.string.excellent));
		} else if (tmp > 0.5) {
			instructions.setText(getResources().getString(R.string.good));
		} else if (tmp > 0.01) {
			instructions.setText(getResources().getString(R.string.fair));
		} else if (tmp == -1) {
			instructions.setText("skip");
		} else {
			instructions.setText(getResources().getString(R.string.almost));
		}
		if (tmp != -1) {
			input.setTextColor(getResources().getColor(R.color.green));
			input.setBackgroundColor(getResources().getColor(R.color.lightgreen));
		}
		input.setText(q.questions[0][0]);
		spacer.updateAnswer(true, getCurrentTime());
		waitAfterAnswer();
	}

	private void wrongAnswer() {
		instructions.setText(getResources().getString(R.string.wrong));
		input.setTextColor(getResources().getColor(R.color.red));
		input.setBackgroundColor(getResources().getColor(R.color.lightred));
		input.setText(q.questions[0][0]);
		input.startAnimation(shake);
		spacer.updateAnswer(false, getCurrentTime());
		waitAfterAnswer();
	}

	/*
	 * if the answer of the user is wrong theres a 5 seconds waiting time
	 */
	private void waitAfterAnswer() {
		countDownTimer = new CountDownTimer(2000, 2000) {
			@Override
			public void onTick(long millisUntilFinished_) {
			}
			@Override
			public void onFinish() {
				clickNext(null);
			}
		}.start();
	}

	private boolean checkAnswer(String answer, String correct_answer) {
		try {
			int tmp = Integer.parseInt(answer);
			NumbersToWords ntw = new NumbersToWords();
			answer = ntw.getWord(tmp);
		} catch (Exception ex) {
		}
		correct_answer = removeAccents(correct_answer);
		answer = removeAccents(answer);
		answer = answer.toLowerCase(Locale.US);
		correct_answer = correct_answer.toLowerCase(Locale.US);
		answer = answer.trim();
		correct_answer = correct_answer.trim();
		answer = answer.replaceAll("[^А-Яа-я]+", "");  
		correct_answer = correct_answer.replaceAll("[^А-Яа-я]+", "");
		if (answer.equals(correct_answer)) {
			// System.out.println("correct");
			return true;
		} else {
			// System.out.println("false");
			return false;
		}
	}

	private long getCurrentTime() {
		return System.currentTimeMillis();
	}

	@Override
	public void onBackPressed() {
		if (doubleBackToExitPressedOnce) {
			try {
				countDownTimer.cancel();
			} catch (Exception ex) {
			}
			super.onBackPressed();
			return;
		}
		this.doubleBackToExitPressedOnce = true;
		Toast.makeText(this, getString(R.string.pessback), Toast.LENGTH_SHORT).show();
		new Handler().postDelayed(new Runnable() {
			public void run() {
				doubleBackToExitPressedOnce = false;
			}
		}, 2000);
	}

	private void showDialogSessionComplete() {
		DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				switch (which) {
				case DialogInterface.BUTTON_POSITIVE:
					// Yes button clicked
					finish();
					break;

				case DialogInterface.BUTTON_NEGATIVE:
					// No button clicked
					stop = false;
					break;
				}
			}
		};
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(getString(R.string.complete))
				.setPositiveButton(getString(R.string.yes), dialogClickListener)
				.setNegativeButton(getString(R.string.no), dialogClickListener).show();
	}

	public void onBeginningOfSpeech() {
		try {
			countDownTimeOut.cancel();
		} catch (Exception ex) {
		}
	}

	public void onBufferReceived(byte[] arg0) {
	}

	public void onEndOfSpeech() {
		canSkip = false;
		String uri = "drawable/mic";
		int imageResource = getResources().getIdentifier(uri, null, getPackageName());
		Drawable image = getResources().getDrawable(imageResource);
		mic.setImageDrawable(image);
		instructions.setText(getResources().getString(R.string.processing));
		speech.stopListening();
	}

	public void onError(int e) {
		try {
			countDownTimeOut.cancel();
		} catch (Exception ex) {
		}
		if (e == 1) { // ERROR_NETWORK_TIMEOUT
			showNoInternetDialog();
		}
		if (e == 2) { // ERROR_NETWORK
			showNoInternetDialog();
		}
		if (e == 3) { // ERROR_AUDIO
		}
		if (e == 4) { // ERROR_SERVER
			showNoInternetDialog();
		}
		if (e == 5) { // ERROR_CLIENT
		}
		if (e == 6) { // ERROR_SPEECH_TIMEOUT
		}
		if (e == 7) { // ERROR_NO_MATCH
			canSkip = true;
			setMic(false);
			instructions.setText(getResources().getString(R.string.mic));
		}
		if (e == 8) { // ERROR_RECOGNIZER_BUSY
			speech.cancel();
			new Handler().postDelayed(new Runnable() {
				public void run() {
					speech.startListening(intent);
				}
			}, 1000);
		}
		//Log.e("SPEECH", "onError " + e);
	}

	public void onEvent(int arg0, Bundle arg1) {
		//Log.e("SPEECH", "onEvent");
	}

	public void onPartialResults(Bundle arg0) {
		//Log.e("SPEECH", "onPartialResults");
	}

	public void onReadyForSpeech(Bundle arg0) {
		timeOut();
		setMic(true);
		instructions.setText(getResources().getString(R.string.speak));
		//Log.e("SPEECH", "onReadyForSpeech");
	}

	public void onResults(Bundle data) {
		speak();
		ArrayList<String> matches = data.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
		float[] scores = data.getFloatArray(SpeechRecognizer.CONFIDENCE_SCORES);
		float tmp = isCorrect(matches, scores);
		if (tmp > 0) {
			correctAnswer(tmp);
		} else {
			wrongAnswer();
		}
	}

	private float isCorrect(ArrayList<String> matches, float[] scores) {
		if(matches == null){
			return 0;
		}
		
		for (int i = 0; i < matches.size(); i++) {
			if (checkAnswer(matches.get(i), q.questions[0][0])) {
				if (scores == null) {
					return (float) 0.75;
				} else {
					if (Float.isNaN(scores[0])){
						return (float) 0.75;
					}
					else {
						return scores[0];
					}
					
				}
				
				
			} else {
				for (int j = 0; j < words.size(); j++) {
					if (checkAnswer(matches.get(i), words.get(j).getWord1())) {
						return 0;
					}
				}
			}
		}
		int levenshteinDistance = Integer.MAX_VALUE;
		int indexLowest = 0;
		for (int i = 0; i < words.size(); i++) {
			int tmpDistance = LevenshteinDistance.computeLevenshteinDistance(matches.get(0), words.get(i).getWord1());
			if (tmpDistance < levenshteinDistance) {
				levenshteinDistance = tmpDistance;
				indexLowest = i;
			}
		}
		if (q.questions[0][0].equals(words.get(indexLowest).getWord1())) {
			return (float) 0.01;
		} else {
			return 0;
		}
	}

	// volume
	public void onRmsChanged(float arg0) {
		view.height=mic.getHeight();
		view.width=mic.getWidth();
		
		view.size = (arg0 / 10);
	}

	public void pressMic(View v) {
		speech.startListening(intent);
	}

	public void onInit(int status) {
		// TODO Auto-generated method stub
		// TTS is successfully initialized
		if (status == TextToSpeech.SUCCESS) {
			useTTS = true;
			// Setting speech language
			Locale loc = new Locale("rus", "RUS");
			int result = tts.setLanguage(loc);
			// If your device doesn't support language you set above
			if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
				showDialogTTS();
				//soundOn.setVisible(false);
				//soundOff.setVisible(false);
				
				// Cook simple toast message with message
				// Toast.makeText(this, "Language not supported",
				// Toast.LENGTH_LONG).show();
				// Log.e("TTS", "Language is not supported");
			}

			else {

			}
			// TTS is not initialized properly
		} else {
			showDialogTTS();
			
			useTTS = false;
			//soundOn.setVisible(false);
			//soundOff.setVisible(false);
			
			// Toast.makeText(this, "TTS Initilization Failed",
			// Toast.LENGTH_LONG).show();
			// Log.e("TTS", "Initilization Failed");
		}
	}

	private void showDialogTTS() {
		SharedPreferences sp = getSharedPreferences("my_db", Context.MODE_PRIVATE);

		if(sp.getBoolean("tts", true)){
		
		DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				switch (which) {
				case DialogInterface.BUTTON_POSITIVE:
					// Yes button clicked
			        Intent installIntent = new Intent();
			        installIntent.setAction(
			        TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
			        startActivity(installIntent);	
					break;

				case DialogInterface.BUTTON_NEGATIVE:
					// No button clicked
					
					break;
				
				case DialogInterface.BUTTON_NEUTRAL:
					SharedPreferences sp = getSharedPreferences("my_db", Context.MODE_PRIVATE);
					Editor e = sp.edit();
					e.putBoolean("tts", false);
					e.commit();
					break;
					
					
				}
				
				
				
				
			}
		};
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(getString(R.string.notts)).setNeutralButton(getString(R.string.notagain), dialogClickListener)
				.setPositiveButton(getString(R.string.yes), dialogClickListener)
				.setNegativeButton(getString(R.string.no), dialogClickListener).show();
		}
	}

	private void timeOut() {
		countDownTimeOut = new CountDownTimer(5000, 5000) {
			@Override
			public void onTick(long millisUntilFinished) {
			}

			@Override
			public void onFinish() {
				setMic(false);
				canSkip = true;
				// speech.stopListening();
				speech.cancel();
				// speech.stopListening();
				instructions.setText(getResources().getString(R.string.mic));

			}
		}.start();
	}

	private void setMic(boolean on) {
		if (on) {
			String uri = "drawable/mic_blue";
			int imageResource = getResources().getIdentifier(uri, null, getPackageName());
			Drawable image = getResources().getDrawable(imageResource);
			mic.setImageDrawable(image);

		} else {
			String uri = "drawable/mic";
			int imageResource = getResources().getIdentifier(uri, null, getPackageName());
			Drawable image = getResources().getDrawable(imageResource);
			mic.setImageDrawable(image);
		}
	}

	@Override
	protected void onDestroy() {
		if (tts != null) {
			try {
				tts.stop();
				tts.shutdown();
			} catch (Exception ex) {

			}

		}

		if (speech != null) {
			try {
				speech.destroy();
			} catch (Exception ex) {

			}

		}
		super.onDestroy();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Used to put dark icons on light action bar
		SharedPreferences sp = getSharedPreferences("my_db", Context.MODE_PRIVATE);

		sound = sp.getBoolean("sound", true);

		skip = menu.add("skip");
		skip.setIcon(R.drawable.skip);
		skip.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);

		soundOn = menu.add("Sound off");
		soundOn.setIcon(R.drawable.volume_on);
		soundOn.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
		soundOn.setVisible(sound);

		soundOff = menu.add("Sound on");
		soundOff.setIcon(R.drawable.volume_mute);
		soundOff.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
		soundOff.setVisible(!sound);

		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// This uses the imported MenuItem from ActionBarSherlock
		// Toast.makeText(this, "Got click: " + item.toString(),
		// Toast.LENGTH_SHORT).show();
		if (item.getTitle() == "Sound off") {
			soundOn.setVisible(false);
			soundOff.setVisible(true);
			sound = false;
		}
		if (item.getTitle() == "Sound on") {
			soundOn.setVisible(true);
			soundOff.setVisible(false);
			sound = true;
		}
		if (item.getTitle() == "skip") {
			if (canSkip) {
				canSkip = false;
				try {
					speak();
					speech.stopListening();
					speech.cancel();
					countDownTimeOut.cancel();
					setMic(false);

				} catch (Exception ex) {

				}
				correctAnswer(-1);
			}
		}

		SharedPreferences sp = getSharedPreferences("my_db", Context.MODE_PRIVATE);
		Editor e = sp.edit();
		e.putBoolean("sound", sound);
		e.commit();

		return true;
	}

	private void speak() {
		if (useTTS) {
			if (sound) {
				tts.speak(q.questions[2][0], TextToSpeech.QUEUE_FLUSH, null);
			}
		}
	}

}